﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Collections.Specialized;
using System.Web.Mvc;
using System.Text.RegularExpressions;
using System.IO;
using System.Web;
using System.Runtime.Caching;
using Microsoft.Scripting.Hosting;
using Ruandao.HostServer;

namespace Ruandao.MiniFace
{

    public delegate string MapPathFunction(string url);
    public delegate string GetUrlBaseFunction();

    public class Template
    {
        public static MapPathFunction MapPath; 
        public static GetUrlBaseFunction GetUrlBase;

        public string TemplateUrlBase { get; set; }  // 模板的基准路径
        public string FilePath { get; set; }         // 相对于模板基准的模板文件的路径

        private List<Content> pageContents;          // 模板的内存对象模型

        public Template(string filePath)
        {
            string urlBase = "~/Templates/Default/";
            if (Template.GetUrlBase != null)
                urlBase = Template.GetUrlBase();

            Template template = new Template(urlBase, filePath);
            Create(urlBase, filePath);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="controllerContext"></param>
        /// <param name="templateUrlBase">模板文件所在位置的基准路径。</param>
        /// <param name="filePath">模板文件的相对路径。</param>
        public Template(string templateUrlBase, string filePath)
        {
            Create(templateUrlBase, filePath);

        }

        private void Create(string templateUrlBase, string filePath)
        {
            this.FilePath = filePath;
            this.TemplateUrlBase = templateUrlBase.EndsWith("/") ? templateUrlBase : templateUrlBase + "/";

            string url = Ruandao.Misc.UtilityFunction.CombineUrl(this.TemplateUrlBase, this.FilePath);
            string file = GetPhysicalPath(url);
            ObjectCache cache = MemoryCache.Default;
            pageContents = cache["TEMPLATE_CONTENTS_" + file] as List<Content>;

            if (pageContents == null)
            {
                StringBuilder html = new StringBuilder();
                using (StreamReader reader = new StreamReader(file))
                {
                    html.Append(reader.ReadToEnd());
                }

                if (html.Length == 0)
                {
                    html.Append("没有读取到模板，或者模板为空。");
                }

                // 替换模板路径和应用程序路径的占位符。
                html.Replace("{~}/", VirtualPathUtility.ToAbsolute("~/"));
                html.Replace("{^}/", VirtualPathUtility.ToAbsolute(this.TemplateUrlBase));
                // 下面的做法和ie保存网页的做法一致，也是使用.htm和_files的命名约定。
                string ext = ".htm";
                if (url.EndsWith(".html")) ext = ".html";
                html.Replace("{.}/",  VirtualPathUtility.ToAbsolute(url.Replace(ext, "_files/")));

                CacheItemPolicy policy = new CacheItemPolicy();
                List<string> filePaths = new List<string>();
                filePaths.Add(file);

                policy.ChangeMonitors.Add(new HostFileChangeMonitor(filePaths));

                // 解析模板文件.
                pageContents = ParseContent(html.ToString());

                cache.Set("TEMPLATE_CONTENTS_" + filePath, pageContents, policy);
            }
        }

        public static string GetPhysicalPath(string url)
        {
            string file;
            if (Template.MapPath == null)
            {
                file = HttpContext.Current.Server.MapPath(url);
            }
            else
            {
                file = Template.MapPath(url);
            }
            return file;
        }

        // 
        public void Render(TemplateContext templateContext, TextWriter writer)
        {
            foreach (Content content in pageContents)
            {
                content.Render( templateContext, writer);
            }
        }

        private List<Content> ParseContent(string html)
        {
            List<Content> contents = new List<Content>();

            string pattern = @"\{\*.*?\*\}|\{=.*?=\}|\{%.*?%\}|<\$.*?\$>|<@.*?@>";
            foreach (var segment in new Traversal(html, pattern))
            {
                if (!segment.IsTarget)
                {
                    contents.Add(new TextItem(this, segment.Text));
                    continue;
                }
                string prefix = segment.Text.Substring(0, 2);
                string middle = segment.Text.Substring(2, segment.Text.Length - 4);
                switch (prefix)
                {
                    case "{*":
                        contents.Add(new CommentText(this,middle));
                        break;
                    case "{=":
                        contents.Add(new ExpressionItem(this, middle));
                        break;
                    case "{%":
                        contents.Add(new ScriptBlock(this, middle));
                        break;
                    case "<$":
                        contents.Add(new WidgetElement(this, "<" + middle + ">"));
                        break;
                    case "<@":
                        contents.Add(new Directive(this, "<" + middle + ">"));
                        break;
                }

            }
            return contents;
        }


    }
}
